package com.gitee.algorithm.tree;

import java.util.Queue;

/*
树的实现也可以使用数组或者链表，这里采用链表
二叉查找树
 */
public class BinaryTree <Key extends Comparable<Key>,Value> {
    //记录根节点
    private Node root;
    //记录树中元素的个数
    private int N;

    private class Node {
        //存储键
        public Key key;
        //存储值
        private Value value;
        //记录左子节点
        public Node left;
        //记录右子节点
        public Node right;

        public Node(Key key, Value value, Node left, Node right) {
            this.key = key;
            this.value = value;
            this.left = left;
            this.right = right;
        }
    }

    public int size() {
        return N;
    }

    //向树中添加元素key-value
    public void put(Key key, Value value) {
        root = put(root, key, value);
    }

    //向指定的数X中添加key-value，并返回添加的元素后的新树
    private Node put(Node x, Key key, Value value) {
        //如果X子树为空
        if (x == null) {
            N++;
            return new Node(key, value, null, null);
        }
        //如果X子树不为空
        //比较X结点的键和key的大小
        int cmp = key.compareTo(x.key);
        if (cmp > 0) {
            //如果key大于x结点的键，则继续找x结点的右子树
            x.right = put(x.right, key, value);
        } else if (cmp < 0) {
            //如果key小于x结点的键，则继续找x结点的左子树
            x.left = put(x.left, key, value);
        } else {
            //如果key等于x结点的键，则替换x结点的value即可
            x.value = value;
        }
        return x;
    }

    //根据key，从树中找出对应的值
    public Value get(Key key) {
        return get(root, key);
    }

    //从指定的树x中，找出key对应的值
    private Value get(Node x, Key key) {
        //x树为null
        if (x == null) {
            return null;
        }
        //x树不为null
        //比较key和x结点的键的大小
        int cmp = key.compareTo(x.key);
        if (cmp > 0) {
            //如果key大于x结点的键，则继续找x结点的右子树
            return get(x.right, key);
        } else if (cmp < 0) {
            //如果key小于x结点的键，则继续找x结点的左子树
            return get(x.left, key);
        } else {
            //如果key等于x结点的键，则就找到了键为key的结点，只需要返回x结点的值即可
            return x.value;
        }
    }

    //删除.根据key，删除树中对应的键值对
    public void delate(Key key) {
        root = delete(root, key);
    }

    //删除指定树x上的键为key的键值对，并返回删除后的新树
    public Node delete(Node x, Key key) {
        if (x == null) {
            return null;
        }
        int cmp = key.compareTo(x.key);
        if (cmp > 0) {
            //新结点的key值大于当前结点的key，继续找当前结点的右子节点
            x.right = delete(x.right, key);
        } else if (cmp < 0) {
            //新结点的key小于当前结点的key，继续找当前结点的左子节点
            x.left = delete(x.left, key);
        } else {
            //新结点的key等于当前结点，当前x值就是要删除的结点
            //1.如果当前结点的右子树不存在，则直接返回当前结点的左子节点
            if (x.right == null) {
                N--;
                return x.left;

            }
            //2.如果当前结点的左子树不存在，则直接返回当前结点的右子节点
            if (x.left == null) {
                N--;
                return x.right;
            }
            //3.当前结点的左右子树都存在
            //3.1右子树的最小结点
            Node minNode = x.right;
            while (minNode.left != null) {
                minNode = minNode.left;
            }
            //3.2删除右子树中中最小结点
            Node n = x.right;
            while (n.left != null) {
                if (n.left.left == null) {
                    n.left = null;
                } else {
                    n = n.left;
                }
            }
            //3.3让被删除结点的左子树成为minNode的左子树，让被删除结点的右子树成为minNode的右子树
            minNode.left = x.left;
            minNode.right = x.right;
            //3.4让被删结点的父结点指向minNode结点
            x = minNode;
            //个数减一
            N--;
        }
        return x;
    }

    //查找树中最小键
    public Key min(){
        return min(root).key;
    }

    //查找指定树x中最小键对应的结点
    private Node min(Node x){
        //如果左子节点没有结点当前结点就是最小结点，如果有继续找
        if(x.left!=null){
            return min(x.left);
        }else {
            return x;
        }
    }

    //查找整个树中最小键
    public Key max(){
        return max(root).key;
    }

    //查找指定树x中最大键对应的结点
    private Node max(Node x){
        //如果右子节点没有结点当前结点就是最大结点，如果有继续找
        if(x.right!=null){
            return max(x.right);
        }else {
            return x;
        }
    }

    //使用前序遍历，获取整个树的所有键
    public com.gitee.algorithm.linear.Queue<Key> preErgodic(){
        com.gitee.algorithm.linear.Queue<Key> keys=new com.gitee.algorithm.linear.Queue<>();
        preErgodic(root,keys);
        return keys;
    }

    //获取指定树x的所有键，并放到keys队列中
    private void preErgodic(Node x, com.gitee.algorithm.linear.Queue<Key> keys){
        if(x==null){
            return;
        }
        //把x结点的key放入到keys中
        keys.enqueue(x.key);
        //递归遍历x结点的左子树
        if(x.left!=null){
            preErgodic(x.left,keys);
        }
        //递归遍历x结点的右子树
        if (x.right!=null){
            preErgodic(x.right,keys);
        }
    }

    //使用中序遍历，获取树中所有的键
    public com.gitee.algorithm.linear.Queue<Key> midErgodic(){
        com.gitee.algorithm.linear.Queue<Key> keys = new com.gitee.algorithm.linear.Queue<>();
        midErgodic(root,keys);
        return keys;
    }

    //使用中序遍历获取指定树x的所有的键，并放到key中
    private void midErgodic(Node x, com.gitee.algorithm.linear.Queue<Key> keys){
        if (x==null){
            return;
        }

        //先递归把左子树的键放到keys中
        if (x.left!=null){
            midErgodic(x.left,keys);
        }

        //把当前结点的x键放到keys中
        keys.enqueue(x.key);

        //再递归把右子树中的键放到keys中
        if (x.right!=null){
            midErgodic(x.right,keys);
        }
    }

    //使用后序遍历，获取整个树中的所有键
    public com.gitee.algorithm.linear.Queue<Key> afterErgodic(){
        com.gitee.algorithm.linear.Queue<Key> keys = new com.gitee.algorithm.linear.Queue<>();
        afterErgodic(root,keys);
        return keys;
    }

    //使用后序遍历，把指定树x中的所有键放入到keys队列中
    private void afterErgodic(Node x, com.gitee.algorithm.linear.Queue<Key> keys){
        if (x==null){
            return;
        }

        // 递归把左子树所有键放入队列中
        if(x.left!=null);{
            afterErgodic(x.left,keys);
        }

        //递归把右子树所有键放入队列中
        if (x.right!=null){
            afterErgodic(x.right,keys);
        }

        //把x结点的键放到keys中
        keys.enqueue(x.key);
    }
}




































































